﻿using CNative.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace CNative
{
    public static partial class Utils
    {
        /// <summary>
        /// 反射对象类型,执行对象内部方法
        /// </summary>
        /// <param name="type">对象类型</param>
        /// <param name="methodName">方法名</param>
        /// <param name="paramters">参数</param>
        /// <returns>object</returns>
        public static object InvokeMethod(this Type type, string methodName, params object[] paramters)
        {
            return Utilities.FastReflection.FastInvoke(type, methodName, paramters);
        }

        /// <summary>
        /// 反射执行对象内部方法
        /// </summary>
        /// <param name="target">对象</param>
        /// <param name="methodName">方法名</param>
        /// <param name="paramters">参数</param>
        /// <returns>object</returns>
        public static object InvokeMethod(this object target, string methodName, params object[] paramters)
        {
            return Utilities.FastReflection.FastInvoke(target, methodName, paramters);
        }

        /// <summary>
        /// 转换为对应的Nullable'T'类型
        /// </summary>
        /// <param name="type">值类型</param>
        /// <returns></returns>
        public static Type ToNullableType(this Type type)
        {
            if (type.IsValueType)
            {
                string fullName =
                    $"System.Nullable`1[[{type.FullName}, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]";

                return Type.GetType(fullName);
            }
            else
                return type;
        }

        readonly static Dictionary<Type, List<Enum>> enumValuesCache = new Dictionary<Type, List<Enum>>();

        /// <summary>
        /// 获取枚举数组
        /// </summary>
        /// <param name="enumType">枚举类型</param>
        /// <returns></returns>
        public static List<Enum> GetEnumValues(this Type enumType)
        {
            return GetEnumValues(enumType, true);
        }

        /// <summary>
        /// 获取枚举数组
        /// </summary>
        /// <param name="enumType">枚举类型</param>
        /// <param name="useCache">是否缓存</param>
        /// <returns></returns>
        public static List<Enum> GetEnumValues(this Type enumType, bool useCache)
        {
            if (useCache)
            {
                lock (enumValuesCache)
                {
                    List<Enum> cacheItem;
                    if (enumValuesCache.TryGetValue(enumType, out cacheItem))
                    {
                        return cacheItem;
                    }
                }
            }

            List<Enum> enumValues = new List<Enum>();
            foreach (Enum item in Enum.GetValues(enumType))
            {
                enumValues.Add(item);
            }

            if (useCache)
            {
                lock (enumValuesCache)
                {
                    enumValuesCache[enumType] = enumValues;
                }
            }

            return enumValues;
        }

        /// <summary>
        /// 获取枚举描述数组
        /// </summary>
        /// <param name="enumType"></param>
        /// <returns></returns>
        public static string[] GetEnumNames(this Type enumType)
        {
            return Enum.GetNames(enumType);
        }
        /// <summary>
        ///  将一个或多个枚举常数的名称或数字值的字符串表示转换成等效的枚举对象
        /// </summary>
        /// <typeparam name="TEnum">要将 <paramref name="enumValue" /> 转换到的枚举类型。</typeparam>
        /// <param name="enumValue">要转换的枚举名称或基础值的字符串表示形式</param>
        /// <returns></returns>
        public static string ToEnumString<TEnum>(this string enumValue) where TEnum : struct
        {
            try
            {
                if (Enum.TryParse<TEnum>(enumValue.NullToStr(), out TEnum enumStr))
                {
                    return enumStr.ToString();
                }
            }
            catch
            {
            }

            return enumValue;
        }
        //--------------------------------------------------------------------------------------------------------
        public static Type GetRootBaseType(this Type entityType)
        {
            var baseType = entityType.BaseType;
            while (baseType != null && baseType.BaseType != typeof(object))
            {
                baseType = baseType.BaseType;
            }
            return baseType;
        }


        /// <summary>验证是否为虚属性</summary>
        /// <param name="property">属性信息</param>
        /// <returns>指示是否为虚属性</returns>
        public static bool IsVirtual(this PropertyInfo property)
        {
            var get = property?.GetGetMethod();
            if (get != null && get.IsVirtual) return true;
            var set = property?.GetSetMethod();
            if (set != null && set.IsVirtual) return true;
            return false;
        }
        /// <summary>
        /// 获取子类型
        /// </summary>
        /// <param name="oldType"></param>
        /// <returns></returns>
        public static Type GetUnderType(this Type oldType)
        {
            bool isNullable = false;
            return GetUnderType(oldType, ref isNullable);
        }
        /// <summary>
        /// 获取子类型
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <param name="isNullable"></param>
        /// <returns></returns>
        public static Type GetUnderType(this Type oldType, ref bool isNullable)
        {
            Type unType = Nullable.GetUnderlyingType(oldType);
            isNullable = unType != null;
            return unType == null ? oldType : unType;
        }
        /// <summary>
        /// 获取子类型
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <param name="isNullable"></param>
        /// <returns></returns>
        public static Type GetUnderType(this PropertyInfo propertyInfo, ref bool isNullable)
        {
            Type unType = Nullable.GetUnderlyingType(propertyInfo.PropertyType);
            isNullable = unType != null;
            unType = unType ?? propertyInfo.PropertyType;
            return unType;
        }
        /// <summary>
        /// 获取子类型
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <returns></returns>
        public static Type GetUnderType(this PropertyInfo propertyInfo)
        {
            bool isNullable = false;
            return GetUnderType(propertyInfo, ref isNullable);
        }
        /// <summary>
        /// 支持可以赋值为null的值类型
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <returns></returns>
        public static bool IsNullable(this PropertyInfo propertyInfo)
        {
            Type unType = Nullable.GetUnderlyingType(propertyInfo.PropertyType);
            return unType != null;
        }
        /// <summary>
        /// 支持可以赋值为null的值类型
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static bool IsNullable(this Type type)
        {
            Type unType = Nullable.GetUnderlyingType(type);
            return unType != null;
        }
        /// <summary>验证是否为可 null 类型</summary>
		/// <typeparam name="T">数据类型</typeparam>
		/// <param name="value">要验证的对象</param>
		/// <returns>指示是否为可 null 类型</returns>
		public static bool IsNullable<T>(this T value)
        {
            return value?.GetType().IsNullable() == true;
        }

        public static bool IsClass(this Type thisValue)
        {
            return thisValue != typeof(string) && thisValue.IsClass;
        }

        /// <summary>验证是否为结构数据</summary>
        /// <param name="type">要验证的类型</param>
        /// <returns>指示是否为结构数据</returns>
        public static bool IsStruct(this Type type)
        {
            return ((type.IsValueType && !type.IsEnum) && (!type.IsPrimitive && !type.IsSerializable));
        }

        /// <summary>验证是否为结构数据</summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="value">要验证的对象</param>
        /// <returns>指示是否为结构数据</returns>
        public static bool IsStruct<T>(this T value)
        {
            return typeof(T).IsStruct();
        }

        public static T IsNullReturnNew<T>(T returnObj) where T : new()
        {
            if (returnObj.IsNullOrEmpty())
            {
                returnObj = FastReflection.FastInstance<T>();
            }
            return returnObj;
        }

#if net40
        public static Type GetTypeInfo(this Type typeInfo)
        {
            return typeInfo;
        }
#endif

        /// <summary>
        /// 返回类型指定方法信息
        /// </summary>
        /// <param name="type"></param>
        /// <param name="name"></param>
        /// <param name="paramters"></param>
        /// <returns></returns>
        public static MethodInfo GetMethod(this Type type, string name, params object[] paramters)
        {
            var reval = FastReflection.GetMethodInfo(type, name, paramters);
            return reval;
        }
        /// <summary>
        /// 返回类型构造函数
        /// </summary>
        /// <param name="type"></param>
        /// <param name="types"></param>
        /// <returns></returns>
        public static ConstructorInfo GetConstructor(this Type type, params object[] types)
        {
            var reval = FastReflection.GetConstructor(type,types);
            return reval;
        }
#if net40
        public static T GetCustomAttribute<T>(this MemberInfo element) where T : Attribute
        {
            var atrs = element.GetCustomAttributes(typeof(T), true);
            return atrs?.Length > 0 ? atrs[0] as T : default(T);
        }
#endif

        /// <summary>
        ///     Checks whether <paramref name="child" /> implements/inherits <paramref name="parent" />
        /// </summary>
        /// <param name="child">Current Type</param>
        /// <param name="parent">Parent Type</param>
        /// <returns></returns>
        public static bool IsInheritsOrImplements(this Type child, Type parent)
        {
            if (child == parent)
            {
                return false;
            }

            if (child.GetTypeInfo().IsSubclassOf(parent))
            {
                return true;
            }

            Type[] parameters = parent.GetGenericArguments();

            bool isParameterLessGeneric = !(parameters.Length > 0 &&
                                            (parameters[0].GetTypeInfo().Attributes & TypeAttributes.BeforeFieldInit) ==
                                            TypeAttributes.BeforeFieldInit);

            while (child != null && child != typeof(object))
            {
                Type cur = GetFullTypeDefinition(child);
                if (parent == cur ||
                    isParameterLessGeneric &&
                    cur.GetInterfaces().Select(GetFullTypeDefinition).Contains(GetFullTypeDefinition(parent)))
                {
                    return true;
                }

                if (!isParameterLessGeneric)
                {
                    if (GetFullTypeDefinition(parent) == cur && !cur.GetTypeInfo().IsInterface)
                    {
                        if (VerifyGenericArguments(GetFullTypeDefinition(parent), cur))
                        {
                            if (VerifyGenericArguments(parent, child))
                            {
                                return true;
                            }
                        }
                    }
                    else
                    {
                        if (child.GetInterfaces()
                            .Where(i => GetFullTypeDefinition(parent) == GetFullTypeDefinition(i))
                            .Any(item => VerifyGenericArguments(parent, item)))
                        {
                            return true;
                        }
                    }
                }

                child = child.GetTypeInfo().BaseType;
            }

            return false;
        }

        private static Type GetFullTypeDefinition(Type type)
        {
            return type.GetTypeInfo().IsGenericType ? type.GetGenericTypeDefinition() : type;
        }

        private static bool VerifyGenericArguments(Type parent, Type child)
        {
            Type[] childArguments = child.GetGenericArguments();
            Type[] parentArguments = parent.GetGenericArguments();
            if (childArguments.Length == parentArguments.Length)
            {
                for (var i = 0; i < childArguments.Length; i++)
                {
                    if (childArguments[i].Assembly != parentArguments[i].Assembly ||
                        childArguments[i].Name != parentArguments[i].Name ||
                        childArguments[i].Namespace != parentArguments[i].Namespace)
                    {
                        if (!childArguments[i].GetTypeInfo().IsSubclassOf(parentArguments[i]))
                        {
                            return false;
                        }
                    }
                }
            }

            return true;
        }
    }
}